package com.shuwen.gcdj.common.util.crypt;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.shuwen.gcdj.common.constant.MemberCst;
import com.shuwen.gcdj.common.enums.ErrorCode;
import com.shuwen.gcdj.dto.jwt.JWTVerifyDto;
import org.apache.commons.lang.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Date;
import java.util.Map;
import java.util.Optional;

public class JWTUtil {

    private JWTUtil() {
    }

    private static final Logger log = LoggerFactory.getLogger(JWTUtil.class);

    /**
     * 生成签名, 24小时后过期
     *
     * @param userId     用户id
     * @param deptId     部门id
     * @param randomCode 登录伪随机码
     * @param secret     用户的密码
     * @return 加密的token
     */
    public static String sign(String userId, String deptId, String randomCode, String secret) {
        Algorithm algorithm = Algorithm.HMAC256(secret);
        // 附带userId信息
        String token = JWT.create()
                .withClaim(MemberCst.JWT_CLAIM_KEY_USER_ID, userId)
                .withClaim(MemberCst.JWT_CLAIM_KEY_DEPT_ID, deptId)
                .withClaim(MemberCst.JWT_CLAIM_KEY_RANDOM_CODE, randomCode)
                .withExpiresAt(DateUtils.addHours(new Date(), MemberCst.JWT_TOKEN_EXPIRE_TIME_HOURS))
                .sign(algorithm);
        log.info("JWTUtil.sign(userId = {}, deptId = {}, secret = {}) token = {}", userId, deptId, secret, token);
        return token;
    }

    /**
     * 校验token是否正确
     *
     * @param token  密钥
     * @param secret 用户的密码
     * @return 是否正确
     */
    public static JWTVerifyDto verify(String token, String secret) {
        JWTVerifyDto resp = new JWTVerifyDto();
        if (token == null || token.isEmpty()) {
            log.error("JWTUtil.verify(token = null) token为空，用户未登录");
            resp.setErrorCode(ErrorCode.UNAUTHORIZED);
            return resp;
        }

        try {
            Algorithm algorithm = Algorithm.HMAC256(secret);
            JWTVerifier verifier = JWT.require(algorithm)
                    .build();
            DecodedJWT jwt = verifier.verify(token);
            Map<String, Claim> claims = jwt.getClaims();
            Claim claim = claims.get(MemberCst.JWT_CLAIM_KEY_USER_ID);
            String userId = claim.asString();
            if (userId == null || userId.isEmpty()) {
                log.error("JWTUtil.verify(token = {}) claim.asString() empty userId error", token);
                resp.setErrorCode(ErrorCode.TOKEN_LOST_USER_ID);
                return resp;
            }

            Claim claimDept = claims.get(MemberCst.JWT_CLAIM_KEY_DEPT_ID);
            String deptId = claimDept.asString();

            Claim randomCodeClaim = claims.get(MemberCst.JWT_CLAIM_KEY_RANDOM_CODE);
            String randomCode = randomCodeClaim.asString();

            if (randomCode == null || randomCode.isEmpty()) {
                log.error("JWTUtil.verify(token = {}) randomCodeClaim.asString() empty randomCode error", token);
                resp.setErrorCode(ErrorCode.TOKEN_LOST_RANDOM_CODE);
                return resp;
            }

            resp.setDecodeUserId(userId);
            Optional.ofNullable(deptId).ifPresent(resp::setDecodeDeptId);
            resp.setDecodeRandomCode(randomCode);
            resp.setErrorCode(ErrorCode.SUCCESS);
            return resp;
        } catch (Exception e) {
            log.error("JWTUtil.verify(token = {}) error: {}", token, e.getMessage());
            resp.setErrorCode(ErrorCode.TOKEN_PARSE_ERROR);
            return resp;
        }
    }

    /**
     * 获得token中的userId信息无需secret解密也能获得
     *
     * @return token中包含的用户id
     */
    public static String getUserId(String token) {
        DecodedJWT jwt = JWT.decode(token);
        return jwt.getClaim(MemberCst.JWT_CLAIM_KEY_USER_ID).asString();
    }

    /**
     * 获得token中的deptId信息无需secret解密也能获得
     *
     * @return token中包含的部门id
     */
    public static String getDeptId(String token) {
        DecodedJWT jwt = JWT.decode(token);
        return jwt.getClaim(MemberCst.JWT_CLAIM_KEY_DEPT_ID).asString();
    }
}
